package com.situ.store.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.alibaba.fastjson2.JSON;

/**
 * 封装一个继承了HttpServlet的父类作为Controller通用的分发行为的处理方式整合doGet和doPost行为
 * 通过固定的action参数来识别执行Controller中的哪个方法（action的值就是方法的方法名)
 * 普通controller只需要继承此父类，之后在controller中只需要写对应的业务方法就可以 要求∶1.普通的controller中不要使用方法重载
 * 2.action的值一定要对 3.业务方法中必须只有request和response两个参数??
 *
 * @author dell
 */

public class BaseController extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // 接收一个行为参数
        // 根据参数值判断要做哪个事件（url与方法名一一对应）
        String action = req.getParameter("action");
        // 获取某个Controller类中的所有方法
        Method[] methods = this.getClass().getDeclaredMethods();
        // 当前欲执行的方法
        Method exeMethod = null;
        for (Method m : methods) { // 遍历匹配与行为一直的方法
            if (m.getName().equals(action)) {
                exeMethod = m;
            }
        }
        // 执行方法[核心方法]
        invokeMethod(exeMethod, req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        doGet(req, resp);
    }

    // 接收一个方法，使用反射技术执行这个方法
    private void invokeMethod(Method m, HttpServletRequest req, HttpServletResponse resp) {
        try {
            // 获取m方法的参数列表
            Parameter[] parameters = m.getParameters();
            // 获取m方法的所有参数类型
            Class<?>[] types = m.getParameterTypes();
            // 创建一个长度对应原方法参数个数的空数组(用于存储实际参数的值)
            Object[] params = new Object[types.length];
            // 循环判断参数，根据类型放入对应类型的参数，打破固定两个参数的限制
            for (int i = 0; i < types.length; i++) {
                Parameter p = parameters[i];// 当次循环对应的那个参数

                // ！！Notes：：：配合eclipse设置中Java-----------compiler中的Store information about
                // method parameters（usable via reflection）
                // parameter使用

                String pName = p.getName();// 该参数的参数名
                if (types[i].equals(HttpServletRequest.class)) {
                    params[i] = req;
                } else if (types[i].equals(HttpServletResponse.class)) {
                    params[i] = resp;
                } else if (types[i].equals(HttpSession.class)) {
                    params[i] = req.getSession();
                } else {
                    if (types[i].equals(Integer.class)) {
                        String value = req.getParameter(pName);
                        if (StringUtil.isNotNullAndNotEmpty(value)) {
                            params[i] = Integer.parseInt(value);
                        }
                    } else if (types[i].equals(String.class)) {
                        params[i] = req.getParameter(pName);
                    } else if (types[i].equals(Date.class)) {
                        String value = req.getParameter(pName);
                        if (StringUtil.isNotNullAndNotEmpty(value)) {
                            params[i] = StringUtil.parseDate(value);
                        }
                    } else { // 当参数为自定义类型时
                        params[i] = makeObject(req, types[i]);
                    }

                }
            }
            Object obj = m.invoke(this, params);
            if (obj != null) { // 反射执行controller中的方法时这个方法是有返回值的
                // 有返回值的方法，直接将返回的结果转为JSON格式 传送给前台
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().write(JSON.toJSONString(obj));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 接收请求对象和一个具体的类型
    private Object makeObject(HttpServletRequest req, Class<?> clazz) {
        try {
            Object o = clazz.newInstance();
            Enumeration<String> next = req.getParameterNames(); // 获取请求参数的名字

            while (next.hasMoreElements()) { // 循环枚举所有的名字，返回false代表没有下一个
                String name = next.nextElement(); // 获取出当前这次循环的名字
                String value = req.getParameter(name); // 此参数的值
                try {
                    Field f = clazz.getDeclaredField(name);
                    f.setAccessible(true);
                    if (f.getType().equals(String.class)) {
                        f.set(o, value);
                    } else if (f.getType().equals(Integer.class)) {
                        if (StringUtil.isNotNullAndNotEmpty(value)) {
                            f.set(o, Integer.parseInt(value));
                        }
                    } else if (f.getType().equals(Date.class)) {
                        if (StringUtil.isNotNullAndNotEmpty(value)) {
                            f.set(o, StringUtil.parseDate(value));
                        }
                    } else if (f.getType().equals(BigDecimal.class)) {
                        if (StringUtil.isNotNullAndNotEmpty(value)) {
                            f.set(o, new BigDecimal(value).setScale(2, RoundingMode.HALF_UP));
                        }
                    }
                } catch (Exception e) {
                    // 没有此次循环的成员变量，例如：action
                } // end 内部 try catch
            } // end while
            return o;
        } catch (Exception e) {
            return null;
        } // end 外部 try catch
    } // end makeObject()

}
